/**
 * @file statsInfo.h
 * @author lynxi
 * @brief
 * @version 1.0
 * @date 2024-04-25
 *
 * Copyright:
 * © 2024 北京灵汐科技有限公司 版权所有。
 * 注意：以下内容均为北京灵汐科技有限公司原创，未经本公司允许，不得转载，否则将视为侵权；对于不遵守此声明或者其他违法使用以下内容者，本公司依法保留追究权。
 * © 2024 Lynxi Technologies Co., Ltd. All rights reserved.
 * NOTICE: All information contained here is, and remains the property of Lynxi.
 * This file can not be copied or distributed without the permission of Lynxi
 * Technologies Co., Ltd.
 *
 */

#pragma once

#include <sys/time.h>
#include <atomic>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include "util.hpp"

using namespace std;

// 统计信息
typedef struct {
  string name;
  uint32_t totalNum;
  uint32_t totalTime;  // 微秒
} Stats_Info_T;

// 统计信息类
class StatsInfo {
 public:
  // 初始化统计信息
  StatsInfo(const string &title, const map<uint32_t, std::string> &mapStats)
      : m_bQuit(true), m_title(title) {
    for (const auto &pair : mapStats) {
      Stats_Info_T *info = new Stats_Info_T;
      info->name = pair.second;
      info->totalNum = 0;
      info->totalTime = 0;
      m_mapStatsInfos.insert(make_pair(pair.first, info));
    }
  }

  ~StatsInfo() {
    for (const auto &pair : m_mapStatsInfos) {
      delete pair.second;
    }
  }

  // 开启统计信息打印
  void StartPrint(uint32_t iInterval = 1000 /*毫秒*/) {
    m_bQuit = false;

    m_printThr = move(thread(&StatsInfo::PrintStatsThread, this, iInterval));
  }

  // 停止统计信息打印
  void StopPrint() {
    if (!m_bQuit) {
      m_bQuit = true;
      m_printThr.join();
    }
  }

  // 添加帧
  void UpdateStats(uint32_t id, uint32_t number, uint32_t time /*usec*/) {
    lock_guard<mutex> l(m_mtx);
    auto iter = m_mapStatsInfos.find(id);
    if (iter != m_mapStatsInfos.end()) {
      iter->second->totalNum += number;
      iter->second->totalTime += time;
    }
  }

 protected:
  void PrintStatsThread(uint32_t iInterval /*毫秒*/) {
    uint64_t beginTime = getCurrentTimestamp();
    uint64_t endTime;
    double totalTime;
    while (!m_bQuit) {
      if (iInterval <= 10)
        this_thread::sleep_for(chrono::milliseconds(iInterval));
      else {
        for (int i = 0; !m_bQuit && (i < int(iInterval / 10)); i++) {
          this_thread::sleep_for(chrono::milliseconds(10));
        }
      }

      lock_guard<mutex> l(m_mtx);
      stringstream ss;

      endTime = getCurrentTimestamp();

      ss << "<" << m_title << ">------ " << fixed << setprecision(2);

      for (const auto &pair : m_mapStatsInfos) {
        if (pair.second->totalTime == 0) {  // 计算帧率
          totalTime = endTime - beginTime;

          double value =
              (double)pair.second->totalNum / (double)totalTime * 1000000;
          ss << pair.second->name << ": " << value << " ";
        } else {  // 计算延时
          double value = 0;
          if (pair.second->totalNum > 0)
            value = (double)pair.second->totalTime /
                    (double)pair.second->totalNum / 1000;
          ss << pair.second->name << ": " << value << " ";
        }

        pair.second->totalNum = 0;
        pair.second->totalTime = 0;
      }

      beginTime = endTime;

      std::cout << ss.str() << std::endl;
    }
  }

 private:
  string m_title;
  mutex m_mtx;
  map<uint32_t, Stats_Info_T *> m_mapStatsInfos;
  thread m_printThr;
  atomic_bool m_bQuit;
};
